package service

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"cgs-server/helper"
	"cgs-server/server"
	"cgs-server/server/model"
	"go.mongodb.org/mongo-driver/bson"
	"io"
	"mime/multipart"
)

// 封装一些 bimface 接口的固定请求方法

const (
	apiUrl  = "https://api.bimface.com"
	fileUrl = "https://file.bimface.com"
)

var AccessToken = ""

// bimRequest 带有 access token 请求头的请求
func bimRequest(url string, _config ...bson.M) ([]byte, error) {
	if err := CheckAccessToken(); err != nil {
		return nil, err
	}

	config := bson.M{}

	if len(_config) > 0 {
		config = _config[0]
	}

	if _headers, ok := config["headers"]; ok {
		switch headers := _headers.(type) {
		case map[string]interface{}:
			headers["Authorization"] = "Bearer " + AccessToken
			config["headers"] = headers
		case bson.M:
			headers["Authorization"] = "Bearer " + AccessToken
			config["headers"] = headers
		}
	} else {
		headers := bson.M{
			"Authorization": "Bearer " + AccessToken,
		}
		config["headers"] = headers
	}

	return helper.Request(url, config)
}

// GetAccessToken 获取 access token
func GetAccessToken(appKey, appSecret string) (*model.BimResult, error) {
	//appKey = server.Config.Bimface.AppKey
	//appSecret = server.Config.Bimface.AppSecret
	encodedString := base64.StdEncoding.EncodeToString([]byte(appKey + ":" + appSecret))

	body, err := helper.Request(apiUrl+"/oauth2/token", bson.M{
		"method": "POST",
		"headers": bson.M{
			"Content-Type":  "application/json",
			"Authorization": "Basic " + encodedString,
		},
	})

	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

func CheckAccessToken() error {
	if AccessToken == "" {
		result, err := GetAccessToken(server.Config.Bimface.AppKey, server.Config.Bimface.AppSecret)
		if err != nil {
			return err
		}

		if result.Code != "success" {
			return fmt.Errorf("获取 access token 失败，%s", result.Message)
		}

		if data, ok := result.Data.(map[string]interface{}); ok {
			AccessToken = data["token"].(string)
		}
	}

	return nil
}

// GetViewToken 获取 view token
func GetViewToken(fileId string, otherIds ...string) (*model.BimResult, error) {
	param := "fileId=" + fileId

	body, err := bimRequest(apiUrl + "/view/token?" + param)

	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// GetPolicy 获取 Policy，用来上传文件用
// name 文件的全名，使用URL编码（UTF-8），最多256个字符,(必填)
func GetPolicy(name, sourceId string) (*model.BimResult, error) {
	url := fmt.Sprintf("%s/upload/policy?name=%s", fileUrl, name)
	body, err := bimRequest(url)
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// UploadFileWithPolicy 根据policy凭证在web端上传文件
func UploadFileWithPolicy(data map[string]interface{}) (*model.BimResult, error) {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	host := ""
	name := ""

	for k, v := range data {
		if str, ok := v.(string); ok {
			if k == "host" {
				host = str
			} else {
				err := bodyWriter.WriteField(k, str)
				if err != nil {
					return nil, err
				}
			}
			if k == "name" {
				name = str
			}
		}
	}

	if host == "" {
		return nil, errors.New("host in data is required")
	}

	err := bodyWriter.WriteField("success_action_status", "200")
	if err != nil {
		return nil, err
	}

	// file 字段放在最后
	if f, ok := data["file"]; ok {
		if file, ok := f.(*multipart.FileHeader); ok {
			originFile, err := file.Open()
			if err != nil {
				return nil, err
			}
			defer originFile.Close()

			formFileWriter, err := bodyWriter.CreateFormFile("file", name)
			if _, err := io.Copy(formFileWriter, originFile); err != nil {
				return nil, err
			}
		}
	}

	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return nil, err
	}

	body, err := bimRequest(host, bson.M{
		"method": "POST",
		"headers": bson.M{
			"Content-Type": contentType,
		},
		"body": bodyBuf,
	})
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// UploadFile 上传文件，封装一下上面的两个方法
func UploadFile(file *multipart.FileHeader) (*model.BimResult, error) {
	policyResult, err := GetPolicy(file.Filename, "")
	if err != nil {
		return nil, err
	}
	if policyResult.NotSuccess() {
		return nil, errors.New("get policy fail")
	}

	data, ok := policyResult.Data.(map[string]interface{})
	if !ok {
		return nil, errors.New("policy data is empty")
	}

	param := bson.M{
		"host": data["host"],
		"name": file.Filename,
		"key": data["objectKey"],
		"policy": data["policy"],
		"OSSAccessKeyId": data["accessId"],
		"callback": data["callbackBody"],
		"signature": data["signature"],
		"file": file,
	}

	return UploadFileWithPolicy(param)
}

// GetFileInfo 获取文件信息
func GetFileInfo(fileId string) (*model.BimResult, error) {
	url := fileUrl + "/files/" + fileId

	body, err := bimRequest(url)
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// GetFileList 获取文件列表
func GetFileList(data map[string]interface{}) (*model.BimResult, error) {
	param := ""
	for k, v := range data {
		param += fmt.Sprintf("&%s=%s", k, v)
	}
	param = param[1:]
	url := fileUrl + "/files?" + param
	body, err := bimRequest(url)
	if err != nil {
		return nil, err
	}
	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

func SupportTypes() (*model.BimResult, error) {
	body, err := bimRequest(fileUrl + "/support")
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

func DownloadFile(fileId string) (*model.BimResult, error) {
	url := fileUrl + "/download/url?fileId=" + fileId
	body, err := bimRequest(url)
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

func DeleteFile(fileId string) (*model.BimResult, error) {
	url := fileUrl + "/file?fileId=" + fileId
	body, err := bimRequest(url, bson.M{
		"method": "DELETE",
	})
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// TranslateFile 发起文件转换
func TranslateFile(data map[string]interface{}) (*model.BimResult, error) {
	url := apiUrl + "/translate"

	bytesData, err := helper.ToJSON(data)
	if err != nil {
		return nil, err
	}

	body, err := bimRequest(url, bson.M{
		"method": "PUT",
		"body":   bytes.NewReader(bytesData),
		"headers": bson.M{
			"Content-Type": "application/json",
		},
	})
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}

// GetTranslateState 查询转换状态
func GetTranslateState(fileId string) (*model.BimResult, error) {
	url := apiUrl + "/translate?fileId=" + fileId
	body, err := bimRequest(url)
	if err != nil {
		return nil, err
	}

	result := new(model.BimResult)
	err = helper.FromJSON(body, result)
	return result, err
}
